package com.vmj.heap;

import com.vmj.classfile.entity.ConstantInfo;
import com.vmj.classfile.entity.constantinfo.*;

import java.util.List;

/**
 * Created by yangshoujun on 2017/8/26 下午1:33.
 */
public class RuntimeConstantPool {

    private VMClass clazz;
    private Constant[] consts;

    public Constant getConstant(int index) {
        Constant constant = consts[index];
        if(constant != null) {
            return constant;
        }

        throw new RuntimeException("No consant at index: " + index);
    }

    public static RuntimeConstantPool parseConstantPool(VMClass clazz, com.vmj.classfile.entity.ConstantPool constantPool) {

        List<? extends ConstantInfo> constantInfos = constantPool.getConstantInfos();

        int size = constantInfos.size();
        Constant[] consts = new Constant[size];
        RuntimeConstantPool runtimeCp = new RuntimeConstantPool(clazz, consts);
        for(int i = 1;i < size;i++) {

            ConstantInfo constantInfo = constantInfos.get(i);
            if(constantInfo instanceof ConstantIntegerInfo) {
                ConstantIntegerInfo info = (ConstantIntegerInfo) constantInfo;
                Constant constant = new Constant();
                constant.setValue(info.getValue());
                consts[i] = constant;
            } else if(constantInfo instanceof ConstantFloatInfo) {
                ConstantFloatInfo info = (ConstantFloatInfo) constantInfo;
                Constant constant = new Constant();
                constant.setValue(info.getValue());
                consts[i] = constant;
            } else if(constantInfo instanceof ConstantLongInfo) {
                ConstantLongInfo info = (ConstantLongInfo) constantInfo;
                Constant constant = new Constant();
                constant.setValue(info.getValue());
                consts[i] = constant;
                i++;
            } else if(constantInfo instanceof ConstantDoubleInfo) {
                ConstantDoubleInfo info = (ConstantDoubleInfo) constantInfo;
                Constant constant = new Constant();
                constant.setValue(info.getValue());
                consts[i] = constant;
                i++;
            } else if(constantInfo instanceof ConstantStringInfo) {
                ConstantStringInfo info = (ConstantStringInfo) constantInfo;
                int index = info.getStringIndex();

                String str = constantPool.getUtf8(index);
                if(str == null) {
                    str = "";
                }
                Constant constant = new Constant();
                constant.setValue(str);
                consts[i] = constant;
            } else if(constantInfo instanceof ConstantClassInfo) {
                ConstantClassInfo info = (ConstantClassInfo) constantInfo;
                ClassRef ref = ClassRef.parseClassRef(runtimeCp, info);
                Constant constant = new Constant();
                constant.setValue(ref);
                consts[i] = constant;
            } else if(constantInfo instanceof ConstantFieldRefInfo) {
                ConstantFieldRefInfo info = (ConstantFieldRefInfo) constantInfo;
                FieldRef ref = FieldRef.parseFieldRef(runtimeCp, info);
                Constant constant = new Constant();
                constant.setValue(ref);
                consts[i] = constant;
            } else if(constantInfo instanceof ConstantMethodRefInfo) {
                ConstantMethodRefInfo info = (ConstantMethodRefInfo) constantInfo;
                MethodRef ref = MethodRef.parseMethodRef(runtimeCp, info);
                Constant constant = new Constant();
                constant.setValue(ref);
                consts[i] = constant;

            } else if(constantInfo instanceof ConstantInterfaceMethodRefInfo) {
                ConstantInterfaceMethodRefInfo info = (ConstantInterfaceMethodRefInfo) constantInfo;
                InterfaceMethodRef ref = InterfaceMethodRef.parseInterfaceMethodRef(runtimeCp, info);
                Constant constant = new Constant();
                constant.setValue(ref);
                consts[i] = constant;
            } else {
                // TODO
            }

        }
        return runtimeCp;
    }

    public RuntimeConstantPool(VMClass clazz, Constant[] consts) {
        this.clazz = clazz;
        this.consts = consts;
    }

    public VMClass getClazz() {
        return clazz;
    }

    public Constant[] getConsts() {
        return consts;
    }

    public void setClazz(VMClass clazz) {
        this.clazz = clazz;
    }

    public void setConsts(Constant[] consts) {
        this.consts = consts;
    }
}
